﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Http;
using LeonardoNews.Models;
using LeonardoNews.Repositories;
using LeonardoNews.Server.Models.ArticleModels;
using LeonardoNews.Server.Repositories;
using LeonardoNews.Server.PubNub;
using DropNet;

namespace LeonardoNews.Server.Controllers
{
    public class ArticlesController : ApiController
    {
        private const int ArticlesPerPage = 10;
        private readonly ArticlesRepository articlesRepository;
        private readonly CommentsRepository commentsRepository;
        private readonly VotesRepository votesRepository;

        public ArticlesController()
        {
            this.articlesRepository = new ArticlesRepository();
            this.commentsRepository = new CommentsRepository();
            this.votesRepository = new VotesRepository();
        }

        // GET api/articles
        public IEnumerable<ArticleModel> GetArticles()
        {
            var articles = this.articlesRepository.GetAll().AsEnumerable();
            HashSet<ArticleModel> articleModels = new HashSet<ArticleModel>();
            foreach (var article in articles)
            {
                articleModels.Add(ArticleModel.FromArticle(article));
            }

            return articleModels;
        }

        // GET api/articles/5/details
        [ActionName("details")]
        public ArticleModel GetArticle(int id)
        {
            Article article = this.articlesRepository.GetAll().FirstOrDefault(a => a.ID == id);
            if (article == null)
            {
                throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
            }

            return ArticleModel.FromArticle(article);
        }

        // GET api/articles/5/comments
        [HttpGet]
        [ActionName("comments")]
        public IEnumerable<CommentModel> GetArticleComments(int id)
        {
            Article article = this.articlesRepository.GetAll().FirstOrDefault(a => a.ID == id);
            if (article == null)
            {
                throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
            }
            else
            {
                HashSet<CommentModel> commentModels = new HashSet<CommentModel>();
                foreach (var comment in article.Comments)
                {
                    var currentComment = CommentModel.FromComment(comment);
                    commentModels.Add(currentComment);
                }

                return commentModels;
            }
        }

        // GET api/articles/5/votes
        [HttpGet]
        [ActionName("votes")]
        public IEnumerable<VoteModel> GetArticleVotes(int id)
        {
            Article article = this.articlesRepository.GetAll().FirstOrDefault(a => a.ID == id);
            if (article == null)
            {
                throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
            }
            else
            {
                HashSet<VoteModel> voteModels = new HashSet<VoteModel>();
                foreach (var vote in article.Votes)
                {
                    var currentVote = VoteModel.FromVote(vote);
                    voteModels.Add(currentVote);
                }

                return voteModels;
            }
        }

        // GET api/articles/page?p=5
        [HttpGet]
        [ActionName("page")]
        public IEnumerable<ArticleModel> GetArticlesByPage([FromUri]int p)
        {
            var articles = this.articlesRepository.GetAll().OrderBy(a => a.ID).Skip((p - 1) * ArticlesPerPage).Take(ArticlesPerPage);
            if (articles == null)
            {
                throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
            }
            else
            {
                List<ArticleModel> articleModels = new List<ArticleModel>();
                foreach (var article in articles)
                {
                    articleModels.Add(ArticleModel.FromArticle(article));
                }

                return articleModels;
            }
        }

        // PUT api/Articles/5
        public HttpResponseMessage PutArticle(int id, Article article)
        {
            if (!ModelState.IsValid)
            {
                return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
            }

            if (id != article.ID)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest);
            }

            try
            {
                this.articlesRepository.Update(id, article);
            }
            catch (DbUpdateConcurrencyException ex)
            {
                return Request.CreateErrorResponse(HttpStatusCode.NotFound, ex);
            }

            return Request.CreateResponse(HttpStatusCode.OK);
        }

        // POST api/Articles
        public HttpResponseMessage PostArticle([FromBody]Article article)
        {
            if (ModelState.IsValid)
            {
                User existingAuthor;
                if (article.User.ID != 0)
                {
                    existingAuthor = GetExistingUser(article.User.ID);
                }
                else
                {
                    existingAuthor = GetExistingUser(article.User);
                }

                if (existingAuthor != null)
                {
                    article.User = existingAuthor;
                }

                foreach (var comment in article.Comments)
                {
                    User existingCommentAuthor;
                    if (comment.UserID != 0)
                    {
                        existingCommentAuthor = GetExistingUser(comment.UserID);
                    }
                    else
                    {
                        existingCommentAuthor = GetExistingUser(comment.User);
                    }

                    if (existingCommentAuthor != null)
                    {
                        comment.User = existingCommentAuthor;
                    }
                }

                foreach (var vote in article.Votes)
                {
                    User existingVoteAuthor;
                    if (vote.UserID != 0)
                    {
                        existingVoteAuthor = GetExistingUser(vote.UserID);
                    }
                    else
                    {
                        existingVoteAuthor = GetExistingUser(vote.User);
                    }

                    if (existingVoteAuthor != null)
                    {
                        vote.User = existingVoteAuthor;
                    }
                }

                this.articlesRepository.Create(article);

                PubNubPublisher.Publish("A new article has been published!");

                HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, ArticleModel.FromArticle(article));
                response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = article.ID }));
                return response;
            }
            else
            {
                return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
            }
        }

        // POST api/articles/image?articleId=5
        [HttpPost]
        [ActionName("image")]
        public string Post(int articleId)
        {
            var httpRequest = HttpContext.Current.Request;

            if (httpRequest.Files.Count > 0)
            {
                string shareUrl = null;
                foreach (string file in httpRequest.Files)
                {
                    var postedFile = httpRequest.Files[file];
                    shareUrl = UploadInDropbox(postedFile.FileName, postedFile.InputStream, postedFile.ContentLength, true);
                }

                var article = this.articlesRepository.GetAll().FirstOrDefault(a => a.ID == articleId);
                this.articlesRepository.Update(articleId, article);
                return shareUrl;
            }
            else
            {
                throw new ArgumentException("File not uploaded.");
            }
        }

        private const string DropboxAppKey = "lstxwgcdd0holii";
        private const string DropboxAppSecret = "42csmw3g4m13irv";
        private const string DropboxUserKey = "795v44zszfj50ya";
        private const string DropboxUserSecret = "z2u8b3ewejjrdyun";

        public static string UploadInDropbox(string fileName, Stream stream, int fileSize, bool isUserPicture = false)
        {
            DropNetClient client = new DropNetClient(DropboxAppKey, DropboxAppSecret, DropboxUserKey, DropboxUserSecret);
            client.UseSandbox = true;

            var bytes = new byte[fileSize];
            stream.Read(bytes, 0, fileSize);

            var currentTicks = DateTime.Now.Ticks;
            client.UploadFile("/", currentTicks + fileName, bytes);

            if (isUserPicture)
            {
                return client.GetMedia(string.Format("/{0}", currentTicks + fileName)).Url;
            }
            else
            {
                return client.GetShare(string.Format("/{0}", currentTicks + fileName)).Url;
            }
        }

        // DELETE api/Articles/5
        public HttpResponseMessage DeleteArticle(int id)
        {
            Article article = this.articlesRepository.GetSingle(id);
            if (article == null)
            {
                return Request.CreateResponse(HttpStatusCode.NotFound);
            }

            try
            {
                foreach (var vote in article.Votes)
                {
                    this.votesRepository.DeleteVote(vote.ID);
                }

                foreach (var comment in article.Comments)
                {
                    this.commentsRepository.Delete(comment.ID);
                }

                this.articlesRepository.Delete(article);
            }
            catch (DbUpdateConcurrencyException ex)
            {
                return Request.CreateErrorResponse(HttpStatusCode.NotFound, ex);
            }

            return Request.CreateResponse(HttpStatusCode.OK, article);
        }

        private User GetExistingUser(User newUser)
        {
            return UsersRepository.GetAllUsers().FirstOrDefault(u => u.Username == newUser.Username || u.ID == newUser.ID);
        }

        private User GetExistingUser(int userID)
        {
            return UsersRepository.GetAllUsers().FirstOrDefault(u => u.ID == userID);
        }
    }
}